/*
 *  ufmail, a suite of mail servers
 *  Copyright (C) 2013  Jonathon Keogh
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 * 
 *  Jonathon Keogh e-mail: jdkeogh@live.com
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include "log.h"
#include "thread.h"
#include "server-smtp-init.h"
#include "sendf.h"
#include "ufmail.h"
#include "server-smtp-thread.h"

// TODO Create timeout thread: Find out if sDataQueue is NULL - if so, end

void *threadRecv_SMTP(void *vIndex) {
	int iIndex = *((int *)vIndex);
	struct stThread *stMe = stSMTPThreads[iIndex];
	int i, i2;
	char sBuffer[CONFIG_BUFFERSIZE+1];
	char *s;
	unsigned short shHaveDataLock, shBewareLN, shDiscardLN, shProcessNow;

	// We start off with the lock
	shHaveDataLock = 1;

	// Loop
	shBewareLN = 0;
	shProcessNow = 1;
	shDiscardLN = 0;
	while(1) {
		// Wait for data
		memset(&sBuffer, 0, CONFIG_BUFFERSIZE+1);
		i = recv(stMe->iClient, &sBuffer, CONFIG_BUFFERSIZE, 0);

		// What was returned by recv()?
		if(i < 1) {
			// Exit
			writeToLog(LOG_INFO, "(SMTP) Thread %i, recv() did not suceed", iIndex);
			break;
		}

		// Append sDataQueue
		stMe->sDataQueue = realloc(stMe->sDataQueue, strlen(stMe->sDataQueue) +
			strlen(sBuffer) + 1);
		i2 = strlen(stMe->sDataQueue);
		for(i=0;sBuffer[i]!='\0';i++) {
			// Is shBewardLN set? If so, if this is the first character of a recv() and it's \n then it needs to be ignored
			if(i == 0 && shBewareLN == 1 && sBuffer[i] == '\n') {
				// Ignore this character - it might follow a \r from the previous recv()
				continue;
			}
			stMe->sDataQueue[i2+i] = sBuffer[i];
		}
		
		// We've passed the append part - reset shBewareLN
		shBewareLN = 0;

		// Was a CRLF/CR/LF found?
		for(i=0;i<i2;i++) {

			switch(stMe->sDataQueue[i]) {
			case '\r':
				shProcessNow = 1;
				// Is there a \n after this?
				switch(stMe->sDataQueue[i+1]) {
				case '\0':
					// We don't know - but it might be the very first thing that the next recv() gets, so send a warning
					shBewareLN = 1;
					break;
				case '\n':
					// Discard it
					shDiscardLN = 1;
					break;
				}
				break;
			case '\n':
				shProcessNow = 1;
				break;
			}

			// The Server thread should process what we've got up to here, and then look at the rest of the data to see if there is anything else to process
			if(shProcessNow == 1) {
				// Copy the data up to here
				stMe->sProcessNow = realloc(stMe->sProcessNow, i+1);
				memset(stMe->sProcessNow, 0, i+1);
				strncpy(stMe->sProcessNow, stMe->sDataQueue, i);

				// Unlock, and then re-lock to continue
				pthread_mutex_unlock(&stMe->mutex_sProcessNow);
				pthread_mutex_lock(&stMe->mutex_sProcessNow);

				// Clear the data up to here and start again
				s = malloc(i2 - i + 1);
				memset(s, 0, i2 - i + 1);
				strncpy(s, &(stMe->sDataQueue[i+1 + shDiscardLN]), i2 - i); // +shDiscardLN - if shDiscardLN is 1 then it pushes the front forward by 1
				stMe->sDataQueue = realloc(stMe->sDataQueue, i2 - i + 1);
				strncpy(stMe->sDataQueue, s, i2 - i);
				free(s);
				shProcessNow = 0;
				shDiscardLN = 0;
			}
		}
	}

	// TODO Recv finished - do something with this information?
}

void *threadServer_SMTP(void *vIndex) {
	int iIndex = *((int *)vIndex);
	struct stThread *stMe = stSMTPThreads[iIndex];
	char *sProcessNow;

	// Open connection
	sendf(stMe->iClient, "220 %s\r\n", CONFIG_HOSTNAME);
	writeToLog(LOG_INFO, "(SMTP) Thread %i open", iIndex);

	// Receive data continuously
	sProcessNow = malloc(1);
	sProcessNow[0] = 0;
	while(1) {  // TODO While what? Check still connected?
		// Wait for data
		pthread_mutex_lock(&stMe->mutex_sProcessNow);

		// Once the lock is ours, copy the data from stMe->sProcessNow, empty stMe->sProcessNow, remove the lock and then process the copied data
		sProcessNow = realloc(sProcessNow, strlen(stMe->sProcessNow) + 1);
		memset(sProcessNow, 0, strlen(stMe->sProcessNow) + 1);
		strcpy(sProcessNow, stMe->sProcessNow);
		free(stMe->sProcessNow);
		stMe->sProcessNow = malloc(1);
		stMe->sProcessNow[0] = 0;
		pthread_mutex_unlock(&stMe->mutex_sProcessNow);
		writeToLog(LOG_INFO, "(SMTP) Thread %i - data received: %s", iIndex, sProcessNow);

		// TODO Check that thread hasn't timed out?
	}

	// Close connection
	close(stMe->iClient);

	// Reset structure
	free(vIndex);
	stMe->iClient = 0;
	stMe->shInUse = 0;

	// Clean up
	free(sProcessNow);
	
	// TODO Lock sProcessNow and free() it - make it NULL so that the recv thread ends
}
